﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.CodeDom;
using System.Reflection;
using devtm.Editor.TypeSelector.AttributeBuilder;
using System.CodeDom.Compiler;
using System.IO;

namespace devtm.Editor.TypeSelector
{
    public class CodeGenerate
    {

        #region From

        public static XElement FromCode<T>(T attribute)
            where T : Attribute
        {

            var f = new devtm.Editor.TypeSelector.WindowBuildAttribute();
            f.Context.Add(attribute.GetType());

            if (f.SelectType(attribute))
            { 
            
                return f.Serialize();
            
            }

            return new XElement("attribute", new XAttribute("error", string.Empty));

        }

        #endregion

        #region To

        public static string ToCode(XElement node, CodeDomProvider provider)
        {

            if (node.Name.LocalName != "attributes")
                return string.Empty;

            StringBuilder sortie = new StringBuilder();

            foreach (XElement e in node.Elements())
            {
                CodeAttributeDeclaration attr = GenerateAttribute(e);

                if (attr == null)
                    return string.Empty;

                CodeTypeDeclaration class1 = new CodeTypeDeclaration("Class1");
                class1.CustomAttributes.Add(attr);


                StringBuilder s = new StringBuilder();

                using (MemoryStream fs = new MemoryStream())
                {
                    using (StringWriter w = new StringWriter(s))
                    {
                        provider.GenerateCodeFromType(class1, w , new CodeGeneratorOptions());
                    }
                }

                string r = s.ToString().Split('\n')[0];
                sortie.AppendLine(r.Substring(0,r.Length - 1));

            }

            return sortie.ToString();
        }

        private static CodeAttributeDeclaration GenerateAttribute(XElement node)
        {

            if (node.Name.LocalName != "attribute")
                return null;

            string typeName = (string)node.Attribute("type");
            Type type = Type.GetType(typeName);

            if (type == null)
                throw new Exception(string.Format("{0} can't be resolved, make sure the library has referenced", typeName));

            XElement node2 = node.Element("ctr");
            string sign = (string)node2.Attribute("sgn");

            List<CodeAttributeArgument> lst = new List<CodeAttributeArgument>();

            foreach (ConstructorInfo constructor in type.GetConstructors())
            {
                if (BuildTitle(constructor) == sign)
                {

                    foreach (ParameterInfo para in constructor.GetParameters())
                    {

                        string p = node2.Element(para.Name).Value;
                        CodeAttributeArgument o = GetParameter(para.ParameterType, string.Empty, p);
                        if (o != null)
                            lst.Add(o);
                    }

                    break;
                }
            }

            
            foreach (PropertyInfo para in type.GetProperties())
            {

                XElement p1 = node2.Element(para.Name);
                if (p1 == null)
                    continue;

                string p = p1.Value;
                
                CodeAttributeArgument o = GetParameter(para.PropertyType, para.Name, p);
                if (o != null)
                {
                    lst.Add(o);
                }
            }

            CodeAttributeDeclaration attr = new CodeAttributeDeclaration(new CodeTypeReference(type), lst.ToArray());


            return attr;

        }

        private static CodeAttributeArgument GetParameter(Type type, string name, string p)
        {

            if (type.IsPrimitive || type == typeof(string))
            { 
                var i = Parameter.CastValue(type, p);
                if (string.IsNullOrEmpty(name))
                    return new CodeAttributeArgument(new CodePrimitiveExpression(i));
                else
                    return new CodeAttributeArgument(name, new CodePrimitiveExpression(i));
            }
            else if (type == typeof(Type))
            {
                var i = Parameter.CastValue(type, p);
                if (string.IsNullOrEmpty(name))
                    return new CodeAttributeArgument(new CodeTypeOfExpression((Type)i));
                else
                    return new CodeAttributeArgument(name, new CodeTypeOfExpression((Type)i));
            }
            else if (type == typeof(DateTime) || type == typeof(TimeSpan))
            {

                var i = Parameter.CastValue(type, p);
                if (string.IsNullOrEmpty(name))
                    return new CodeAttributeArgument(new CodeArgumentReferenceExpression(""));
                else
                    return new CodeAttributeArgument(name, new CodeArgumentReferenceExpression(""));
                //CodeArgumentDeclarationExpression

            }

            return null;
        }

        private static string BuildTitle(ConstructorInfo ctor)
        {
            StringBuilder s = new StringBuilder();
            bool e = false;

            s.Append(ctor.ReflectedType.Name + " (");

            foreach (ParameterInfo parameter in ctor.GetParameters())
            {
                if (e)
                    s.Append(", ");

                s.Append(parameter.ParameterType.Name + " " + parameter.Name);

                e = true;

            }

            s.Append(")");

            return s.ToString();

        }
        
        #endregion

        #region Null Value

        public static string Null(Type type)
        {
           

                if (type == typeof(DateTime))
                    return "new System.DateTime()";

                else if (type == typeof(TimeSpan))
                    return "new TimeSpan()";

                else
                    return (GetNull(type) ?? "null").ToString();

            
        }

        public static object GetNull(Type type)
        {

            MethodInfo m = typeof(CodeGenerate)
                .GetMethod("GetNullGeneric")
                .MakeGenericMethod(type);

            return m.Invoke(null, null);
        }

        public static object GetNullGeneric<T>()
        {
            return default(T);
        }


        #endregion

    }
}
